function initShader() {
  // 创建顶点着色器
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  // 将顶点着色器源码传入着色器
  gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);
  // 编译顶点着色器
  gl.compileShader(vertexShader);

  // 创建片元着色器
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  // 将片元着色器源码传入着色器
  gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);
  // 编译片元着色器
  gl.compileShader(fragmentShader);

  // 创建着色器程序
  const program = gl.createProgram();
  // 将顶点着色器和片元着色器挂载到着色器程序上
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  // 链接着色器程序
  gl.linkProgram(program);
  // 使用着色器程序
  gl.useProgram(program);

  return program;
}

// 平移矩阵
function getTranslateMatrix(x = 0, y = 0, z = 0) {
  return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1]);
}

// 缩放矩阵
function getScaleMatrix(x = 1, y = 1, z = 1) {
  return new Float32Array([x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1]);
}

// 绕z轴旋转的旋转矩阵
function getRotateMatrix(deg) {
  return new Float32Array([
    Math.cos(deg),
    Math.sin(deg),
    0,
    0,
    -Math.sin(deg),
    Math.cos(deg),
    0,
    0,
    0,
    0,
    1,
    0,
    0,
    0,
    0,
    1,
  ]);
}

// 矩阵复合函数
function mixMatrix(A, B) {
  const result = new Float32Array(16);

  for (let i = 0; i < 4; i++) {
    result[i] =
      A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3];
    result[i + 4] =
      A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7];
    result[i + 8] =
      A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11];
    result[i + 12] =
      A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15];
  }

  return result;
}

// 归一化函数
function normalize(arr) {
  let sum = 0;

  for (let i = 0; i < arr.length; i++) {
    sum += arr[i] * arr[i];
  }
  const middle = Math.sqrt(sum);

  for (let i = 0; i < arr.length; i++) {
    arr[i] = arr[i] / middle;
  }
  return arr;
}

// 叉积函数 获取法向量
function cross(a, b) {
  return new Float32Array([
    a[1] * b[2] - a[2] * b[1],
    a[2] * b[0] - a[0] * b[2],
    a[0] * b[1] - a[1] * b[0],
  ]);
}

// 点积函数 获取投影长度
function dot(a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}

// 向量差
function minus(a, b) {
  return new Float32Array([a[0] - b[0], a[1] - b[1], a[2] - b[2]]);
}

// 视图矩阵获取
function getViewMatrix(
  eyeX,
  eyeY,
  eyeZ,
  lookAtx,
  lookAty,
  lookAtz,
  upx,
  upy,
  upz
) {
  // 视点
  const eye = new Float32Array([eyeX, eyeY, eyeZ]);
  // 目标点
  const lookAt = new Float32Array([lookAtx, lookAty, lookAtz]);
  // 上向量
  const up = new Float32Array([upx, upy, upz]);

  // 确定z轴
  const z = minus(eye, lookAt);

  normalize(z);
  normalize(up);

  // 确定x轴
  const x = cross(z, up);

  // 确定y轴
  const y = cross(x, z);

  return new Float32Array([
    x[0],
    x[1],
    x[2],
    0,
    y[0],
    y[1],
    y[2],
    0,
    z[0],
    z[1],
    z[2],
    0,
    -dot(x, eye),
    -dot(y, eye),
    -dot(z, eye),
    1,
  ]);
}

// 获取正射投影矩阵
function getOrtho(left, right, bottom, top, near, far) {
  return new Float32Array([
    2 / (right - left), 0, 0, 0,
    0, 2 / (top - bottom), 0, 0,
    0, 0, 2 / (near - far), 0,
    -(right + left) / (right - left), -(top + bottom) / (top - bottom), -(near + far) / (near - far), 1,
  ]);
}

// 获取透射投影矩阵
function getPerspective(fov, aspect, far, near) {
  fov = fov * Math.PI / 180;
  return new Float32Array([
    1 / (aspect * Math.tan(fov / 2)), 0, 0, 0,
    0, 1 / Math.tan(fov / 2), 0, 0,
    0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
    0, 0, -1, 0
  ])
}